# This file is part of Xpra.
# Copyright (C) 2015-2024 Antoine Martin <antoine@xpra.org>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

# pylint: disable=line-too-long
# noinspection PyPep8

import struct
import binascii
from typing import Any, TypeAlias
from collections.abc import Callable, Sequence, Iterable

from xpra.common import roundup
from xpra.util.objects import typedict
from xpra.util.str_fn import csv
from xpra.log import Logger
log = Logger("encoding")

# Warning: many systems will fail above 8k because of memory constraints
# encoders can allocate many times more memory to hold the frames..
TEST_LIMIT_W, TEST_LIMIT_H = 8192, 8192


def unhexlify(s: str) -> bytes:
    return binascii.unhexlify(s.replace(" ", "").replace("\n", ""))


def unhex(s: str) -> tuple[bytes, dict[str, Any]]:
    return unhexlify(s), {}


DEFAULT_TEST_SIZE = 128, 128

TEST_COLORS: dict[str, str] = {
    "black": "000000ff",
    "white": "ffffffff",
    "green": "00ff00ff",
    "red": "ff0000ff",
    "blue": "0000ffff",
    "cyan": "00ffffff",
    "magenta": "ff00ffff",
    "yellow": "ffff00ff",
}


TEST_DATA: TypeAlias = Sequence[tuple[bytes, dict[str, Any]]]

# this test data was generated using a 24x16 blank image as input
TEST_COMPRESSED_DATA: dict[str, dict[str, dict[tuple[int, int], TEST_DATA]]] = {
    "h264": {
        "YUV420P": {
            (24, 16): (
                unhex("000000010910000000016742c015da2f97c05a8303035280000003008000001e478b17500000000168ce3c800000010605ffffa7dc45e9bde6d948b7962cd820d923eeef78323634202d20636f7265203136342072333039352062616565343030202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d30207265663d31206465626c6f636b3d303a303a3020616e616c7973653d303a30206d653d646961207375626d653d30207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d30203878386463743d302063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3020746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d30206b6579696e743d333030206b6579696e745f6d696e3d3330207363656e656375743d3020696e7472615f726566726573683d302072635f6c6f6f6b61686561643d302072633d636272206d62747265653d3020626974726174653d323034382072617465746f6c3d312e302071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d34207662765f6d6178726174653d32303438207662765f62756673697a653d31323238206e616c5f6872643d6e6f6e652066696c6c65723d302069705f726174696f3d312e34302061713d30008000000165888406a8930a0003e4d24f80"),
                unhex("000000010930000001419a201aa7"),
                unhex("000000010930000001419a401aa7"),
                unhex("000000010930000001419a601aa7"),
                unhex("000000010930000001419a801aa7"),
            ),
            (128, 128): (
                unhex("000000010910000000016742c015da0811b016a0c0c0d4a0000003002000000791e2c5d40000000168ce3c800000010605ffffb0dc45e9bde6d948b7962cd820d923eeef78323634202d20636f7265203136342072333039352062616565343030202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d30207265663d31206465626c6f636b3d303a303a3020616e616c7973653d303a30206d653d646961207375626d653d30207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d30203878386463743d302063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3020746872656164733d32206c6f6f6b61686561645f746872656164733d3220736c696365645f746872656164733d3120736c696365733d32206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d30206b6579696e743d333030206b6579696e745f6d696e3d3330207363656e656375743d3020696e7472615f726566726573683d302072635f6c6f6f6b61686561643d302072633d636272206d62747265653d3020626974726174653d323034382072617465746f6c3d312e302071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d34207662765f6d6178726174653d32303438207662765f62756673697a653d31323238206e616c5f6872643d6e6f6e652066696c6c65723d302069705f726174696f3d312e34302061713d30008000000165888406a8930a0003e4d24f2727272727275d75d75d75d75d75d75d75d75d75d75d75d7800000016504222101aa24c28000f93493c9c9c9c9c9c9d75d75d75d75d75d75d75d75d75d75d75d75e0"),
                unhex("000000010930000001419a201aa0430000014104268806a810c0"),
                unhex("000000010930000001419a401aa0430000014104269006a810c0"),
                unhex("000000010930000001419a601aa0430000014104269806a810c0"),
                unhex("000000010930000001419a801aa043000001410426a006a810c0"),
            ),
            # gst-launch-1.0 videotestsrc pattern=white !
            #    ! video/x-raw,width=320,height=240,format="I420"
            #    ! x264enc bframes=0 byte-stream=yes cabac=false mb-tree=false me=0 ref=1 speed-preset=ultrafast tune=zerolatency
            #    ! multifilesink location="frame%d.h264"
            (320, 240): (
                unhex("000000016764000dacb60a0fd0800000030080000019478a15700000000168eac7cb0000010605ffff63dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313634202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d32206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d686578207375626d653d34207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3020746872656164733d33206c6f6f6b61686561645f746872656164733d3320736c696365645f746872656164733d3120736c696365733d33206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d31206b6579696e743d696e66696e697465206b6579696e745f6d696e3d353336383730393133207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d32352e352071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e30300080000001658884bfec8379243fc166cd742d58eeed7a7ffe535260f6de404c6b2a0a81cf8d13414a821aecc4815cdd531d64d15e36f259199d0f166d8e5adfbf4d11261d8a801e7bb5eb23f1d83575daec88f4c42a99313316eefaa221c0ed115cc6f628faed7d722062c989d1845ccc2419d69f02ba9f418275b0ce40cc617567cfd88ecca05eb3d8ec9e6e1dd875933073f89b624ff665ed33c56e1e6d8bd54580ade3f4495be80899b7948f824c6280c9db890393b71207530fe59b2276e240e4edc481c9d95754049d276e240e4edc481c9d7cd47247171207276e240e4edc416f66d92c9db890393b71207275e8e5555a1c90393b71207276e20d131cd6a07276e240e4edc481b48603c510393b71207276e240da7e5f1b839207276e240e4edc41e11ef55276e240e4edc481c9d807af959b890393b71207276e2c6236b3d97e789fd57d054442362f46996c0955a3ea38b49a50384338248608dea0c4ca5b19f06a49000001650328884bffec8379243fc166cd742d58eeed7a7ffe535260f6de404c6b2a0a81cf8d13414a821aecc4815cdd531d64d15e36f259199d0f166d8e5adfbf4d11261d8a801e7bb5eb23f1d83575daec88f4c42a99313316eefaa221c0ed115cc6f628faed7d722062c989d1845ccc2419d69f02ba9f418275b0ce40cc617567cfd88ecca05eb3d8ec9e6e1dd875933073f89b624ff665ed33c56e1e6d8bd54580ade3f4495be80899b7948f824c6280c9db890393b71207530fe59b2276e240e4edc481c9d95754049d276e240e4edc481c9d7cd47247171207276e240e4edc416f66d92c9db890393b71207275e8e5555a1c90393b71207276e20d131cd6a07276e240e4edc481b48603c510393b71207276e240da7e5f1b839207276e240e4edc41e11ef55276e240e4edc481c9d807af959b890393b71207276e2c6236b3d97e789fd57d054442362f46996c0955a3ea38b49a50384338248608dea0c4ca5b19f06a490000016501922212ffec8379243fc166cd742d58eeed7a7ffe535260f6de404c6b2a0a81cf8d13414a821aecc4815cdd531d64d15e36f259199d0f166d8e5adfbf4d11261d8a801e7bb5eb23f1d83575daec88f4c42a99313316eefaa221c0ed115cc6f628faed7d722062c989d1845ccc2419d69f02ba9f418275b0ce40cc617567cfd88ecca05eb3d8ec9e6e1dd875933073f89b624ff665ed33c56e1e6d8bd54580ade3f4495be80899b7948f824c6280c9db890393b71207530fe59b2276e240e4edc481c9d95754049d276e240e4edc481c9d7cd47247171207276e240e4edc416f66d92c9db890393b71207275e8e5555a1c90393b71207276e20d131cd6a07276e240e4edc481b48603c510393b71207276e240da7e5f1b839207276e240e4edc41e11ef55276e240e4edc481c9d807af959b890393b71207276e2c6236b3d97e789fd57d054442362f46996c0955a3ea38b49a50384338248608dea0c4ca5b19f06a49"),
                unhex("00000001419a3b108bfffa580103000001410329a3b108bffa58010300000141019268ec422ffa580103"),
                unhex("00000001419a460845fffa580103000001410329a460845ffa58010300000141019269182117fffa580103"),
                unhex("00000001419a66084dfffc84008380000001410329a66084dffc8400838000000141019269982137fffc84008380"),
                unhex("00000001419a86084dfffc84008381000001410329a86084dffc840083810000014101926a182137fffc84008381"),
            ),
        },
        "YUV422P": {
            (24, 16): (
                unhex("00000001677a000abcb317cbc2000003000200000300651e244cd00000000168e970312c8b0000010605ffff56dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313432202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303134202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d35206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d756d68207375626d653d38207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d31206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d2d3220746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d32206b6579696e743d393939393939206b6579696e745f6d696e3d353030303030207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d33382e322071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888404bffe841fc0a667f891ec3d121e72aecb5f"),
            ),
            (128, 128): (
                unhex("00000001677a000bbcb6102342000003000200000300651e2855c00000000168eac7cb0000010605ffff63dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313634202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d32206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d686578207375626d653d34207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3020746872656164733d32206c6f6f6b61686561645f746872656164733d3220736c696365645f746872656164733d3120736c696365733d32206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d31206b6579696e743d696e66696e697465206b6579696e745f6d696e3d353336383730393133207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d32352e352071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e30300080000001658884046fdcfe0bb9ecd9a9eb03bd86a12e98c759aadef0e758b724399e770a4e6982d7f24eed50c22ea4cb1ecaee80075d00000165042221011bffdcfe0bb9ecd9a9eb03bd86a12e98c759aadef0e758b724399e770a4e6982d7f24eed50c22ea4cb1ecaee80075d"),
                unhex("00000001419a3b1052fffeb52a82160000014104268ec414bffeb52a8216"),
                unhex("00000001419a4608297ffeb52a821700000141042691820a5ffeb52a8217"),
                unhex("00000001419a66082b7ffeb52a821600000141042699820adffeb52a8216"),
                unhex("00000001419a86082b7ffeb52a8217000001410426a1820adffeb52a8217"),
            ),
        },
        "YUV444P": {
            (24, 16): (
                unhex("00000001677a000abcb317cbc2000003000200000300651e244cd00000000168e970312c8b0000010605ffff56dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313432202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303134202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d35206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d756d68207375626d653d38207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d31206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d2d3220746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d32206b6579696e743d393939393939206b6579696e745f6d696e3d353030303030207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d33382e322071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888404bffe841fc0a667f891ec3d121e72aecb5f"),
            ),
            (128, 128): (
                unhex("0000000167f4000b9196c2046840000003004000000ca3c50ab80000000168eac71921900000010605ffff63dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313634202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d32206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d686578207375626d653d34207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3620746872656164733d32206c6f6f6b61686561645f746872656164733d3220736c696365645f746872656164733d3120736c696365733d32206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d31206b6579696e743d696e66696e697465206b6579696e745f6d696e3d353336383730393133207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d32352e352071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e30300080000001658884046fdcfe0b77671337ee87d69a9ad78f78266669bfefb6dfffa887fa2dfe57fdb2f2ef19f3afec13fd4a3ac85fe755041d54fe89f0ac38dc590f600000f98100000165042221011bffdcfe0b77671337ee87d69a9ad78f78266669bfefb6dfffa887fa2dfe57fdb2f2ef19f3afec13fd4a3ac85fe755041d54fe89f0ac38dc590f600000f981"),
                unhex("00000001419a3b1052fffeb52a82160000014104268ec414bffeb52a8216"),
                unhex("00000001419a4608297ffeb52a821700000141042691820a5ffeb52a8217"),
                unhex("00000001419a66082b7ffeb52a821600000141042699820adffeb52a8216"),
                unhex("00000001419a86082b7ffeb52a8217000001410426a1820adffeb52a8217"),
            ),
            # generated using:
            # gst-launch-1.0 videotestsrc pattern=white
            # ! video/x-raw,width=320,height=240
            # ! x264enc
            # ! multifilesink location="frame%d.h264"
            (320, 240): (
                unhex("0000000167f4000d9196c141fa1000000300100000030328f142ae0000000168eac71921900000010605ffff63dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313634202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303232202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d32206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d686578207375626d653d34207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d30206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3620746872656164733d33206c6f6f6b61686561645f746872656164733d3320736c696365645f746872656164733d3120736c696365733d33206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d31206b6579696e743d696e66696e697465206b6579696e745f6d696e3d353336383730393133207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d32352e352071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888423ffe381fdff84523c88b1bff86ab0ff62f3b9fc2a3c873f3e81d0331c4254ac090d2de8dd400f38c9bd127f9b8ebc6f53c07fee1c21fe51e173f854790e7e7d03a0663884a958121a5bd1ba801e71937a24ff371d78dea60bc11348965ffe2b9bc013535e30011415025d55195e3795cb79dcd22597ff8ae6f004d4d78c00450540975546578de572de3285595a41b998baf1598e211c7a9e296eb2bf9009569886d943388e1d161d3d2cec1684e2377cda134e6b18e8368ab8b4aacf6377cda134e6b18e8368ab8b4a1ae3d9772c321bd98b32e9dce9f6fe3d9772c321bd98b32e9dce9f695e57cb5eaad998de9a060f7b3ce49af550d5b331bd340c1ef678599f3ddeeea6528c7c29fd1d123be7bbddd4ca518f853fa3a1dce0bb776bc68e91705644a44945ccc4b25f5ddc27bb29f898926f6c79fce579f434fbca43b5f3082ecf5ffec0d3ef28eadfbf282b136fdbb56c88c25bcf9a91589b7eddab644610c4a6f5dce57c37e34ed534a4979a048ece82bc69daa691f70be42e0fe75ecacd8332b8d332f707f3af6566c19939de99f40c5ea119d92e73e4c37e9b283db0528bde39edd4634fd99faca3a0e4b10a0e634fd99faca3a0e4b108ec53e5b718f5d50da66c674a3999886e047cb5e425a71d4737f79afb3a7b03f144524176953cee366dd1b48814633aaba706530af6b6d4135def8d4e0ca615ed6da787d5e645acfbfea1b9f0a88aabcc8b59f7fd4373e14fd5bfd8ff1fa13d87832222f2ff63fc7e84f61e0c88835f3f297cfec1f83746ff6d389da973fb07e0dd1bfd67f5f82e856ee6cbba4dfb9004a92b38ecb63a7d9bf71fbbdd60cf6921d3cc0f7bbcdbac19ed243a7981ef75ffde517bf45ef0303dfe7f77945efd17bc0c0f7f919513dbdfe7f7813defe667c09ef7f9fde04f7bf945f813deff3fbc09ef7f9fde04f7bfcfef027bdfe4f544f6f7f9fde04f7bf999f027bdfe7f7813defe518a27b7bfcfef027bdfe7f7813deff3fbc09ef7f941bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf94ca89edeff3fbc09ef7f9fde04f7bfcfef027bdfe526ff3fbc09ef7f9fde0129f027bdfe7f7813defe53ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f94fbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf952289edeff3fbc09ef7f9fde04f7bfcfef027bdfe556ff3fbc09ef7f9fde0129f027bdfe7f7813defe54ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f95dbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf955a89edeff3fbc09ef7f9fde04f7bfcfef027bdfe57eff3fbc09ef7f9fde0129f027bdfe7f7813defe55ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f965bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf9e22f13147d9456df6e477121d495bca6d7537db91dc420eee97df687f7f54efe627bba5f7da1fdfd53bf954289e9eff3cfc09d37f31be04e7bfcf3f0274dfcab544f577f9efe04edbfcf5f0275dfe7bf813b6ff2c37f9f1e04f1bfcfaf0091f813beff3e7c09e37f2a3bfcfbf027adfe7e7813ceff3efc09eb7f9f9e048bf027bdfe7ef813defe663c09ef7f9fbe04f7bf954dfe7f7813deff3fbc09ef7f9fde04f7bfcfef02477813deff3fbc09ef7f333e04f7bfcfef027bdfcaaeff3fbc09ef7f9fde04f7bfcfef027bdfe7f781243c09ef7f9fde04f7bf999f027bdfe7f7813defe55b7f9fde04f7bfcfef027bdfe7f7813deff3fbc0925e04f7bfcfef027bdfcccf813deff3fbc09ef7f2afbfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf958dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f3bc0a4ade53698f7c0424bbbcf898a3ec9b3a7d9bf71e1eec18ba7181ef427bbaa0f755fdda4874f303df04defa37bbea4f7f1fde0129ef28bdfa2f78181efe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f3a8fc4c51f6515b7db91dc4875256f29b5d4df6e47710a3bba5f7da1fdfd53bf989eee97df687f7f54efe5437f9e5e04e7bfcf4f0272dfe7978139eff3d3c0913e04ebbfcf7f0276dfcc8f813aeff3dfc09db7f2abbfcf8f0278dfe7d78048fc09df7f9f3e04f1bf957dfe7df813d6ff3f3c09e77f9f7e04f5bfcfcf02497813deff3f7c09ef7f331e04f7bfcfdf027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7804a7c09ef7f9fde04f7bfa57100000165032888423fe381fdff84523c88b1bff86ab0ff62f3b9fc2a3c873f3e81d0331c4254ac090d2de8dd400f38c9bd127f9b8ebc6f53c07fee1c21fe51e173f854790e7e7d03a0663884a958121a5bd1ba801e71937a24ff371d78dea60bc11348965ffe2b9bc013535e30011415025d55195e3795cb79dcd22597ff8ae6f004d4d78c00450540975546578de572de3285595a41b998baf1598e211c7a9e296eb2bf9009569886d943388e1d161d3d2cec1684e2377cda134e6b18e8368ab8b4aacf6377cda134e6b18e8368ab8b4a1ae3d9772c321bd98b32e9dce9f6fe3d9772c321bd98b32e9dce9f695e57cb5eaad998de9a060f7b3ce49af550d5b331bd340c1ef678599f3ddeeea6528c7c29fd1d123be7bbddd4ca518f853fa3a1dce0bb776bc68e91705644a44945ccc4b25f5ddc27bb29f898926f6c79fce579f434fbca43b5f3082ecf5ffec0d3ef28eadfbf282b136fdbb56c88c25bcf9a91589b7eddab644610c4a6f5dce57c37e34ed534a4979a048ece82bc69daa691f70be42e0fe75ecacd8332b8d332f707f3af6566c19939de99f40c5ea119d92e73e4c37e9b283db0528bde39edd4634fd99faca3a0e4b10a0e634fd99faca3a0e4b108ec53e5b718f5d50da66c674a3999886e047cb5e425a71d4737f79afb3a7b03f144524176953cee366dd1b48814633aaba706530af6b6d4135def8d4e0ca615ed6da787d5e645acfbfea1b9f0a88aabcc8b59f7fd4373e14fd5bfd8ff1fa13d87832222f2ff63fc7e84f61e0c88835f3f297cfec1f83746ff6d389da973fb07e0dd1bfd67f5f82e856ee6cbba4dfb9004a92b38ecb63a7d9bf71fbbdd60cf6921d3cc0f7bbcdbac19ed243a7981ef75ffde517bf45ef0303dfe7f77945efd17bc0c0f7f919513dbdfe7f7813defe667c09ef7f9fde04f7bf945f813deff3fbc09ef7f9fde04f7bfcfef027bdfe4f544f6f7f9fde04f7bf999f027bdfe7f7813defe518a27b7bfcfef027bdfe7f7813deff3fbc09ef7f941bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf94ca89edeff3fbc09ef7f9fde04f7bfcfef027bdfe526ff3fbc09ef7f9fde0129f027bdfe7f7813defe53ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f94fbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf952289edeff3fbc09ef7f9fde04f7bfcfef027bdfe556ff3fbc09ef7f9fde0129f027bdfe7f7813defe54ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f95dbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf955a89edeff3fbc09ef7f9fde04f7bfcfef027bdfe57eff3fbc09ef7f9fde0129f027bdfe7f7813defe55ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f965bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf9e22f13147d9456df6e477121d495bca6d7537db91dc420eee97df687f7f54efe627bba5f7da1fdfd53bf954289e9eff3cfc09d37f31be04e7bfcf3f0274dfcab544f577f9efe04edbfcf5f0275dfe7bf813b6ff2c37f9f1e04f1bfcfaf0091f813beff3e7c09e37f2a3bfcfbf027adfe7e7813ceff3efc09eb7f9f9e048bf027bdfe7ef813defe663c09ef7f9fbe04f7bf954dfe7f7813deff3fbc09ef7f9fde04f7bfcfef02477813deff3fbc09ef7f333e04f7bfcfef027bdfcaaeff3fbc09ef7f9fde04f7bfcfef027bdfe7f781243c09ef7f9fde04f7bf999f027bdfe7f7813defe55b7f9fde04f7bfcfef027bdfe7f7813deff3fbc0925e04f7bfcfef027bdfcccf813deff3fbc09ef7f2afbfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf958dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f3bc0a4ade53698f7c0424bbbcf898a3ec9b3a7d9bf71e1eec18ba7181ef427bbaa0f755fdda4874f303df04defa37bbea4f7f1fde0129ef28bdfa2f78181efe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f3a8fc4c51f6515b7db91dc4875256f29b5d4df6e47710a3bba5f7da1fdfd53bf989eee97df687f7f54efe5437f9e5e04e7bfcf4f0272dfe7978139eff3d3c0913e04ebbfcf7f0276dfcc8f813aeff3dfc09db7f2abbfcf8f0278dfe7d78048fc09df7f9f3e04f1bf957dfe7df813d6ff3f3c09e77f9f7e04f5bfcfcf02497813deff3f7c09ef7f331e04f7bfcfdf027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7804a7c09ef7f9fde04f7bfa57100000165019222108fe381fdff84523c88b1bff86ab0ff62f3b9fc2a3c873f3e81d0331c4254ac090d2de8dd400f38c9bd127f9b8ebc6f53c07fee1c21fe51e173f854790e7e7d03a0663884a958121a5bd1ba801e71937a24ff371d78dea60bc11348965ffe2b9bc013535e30011415025d55195e3795cb79dcd22597ff8ae6f004d4d78c00450540975546578de572de3285595a41b998baf1598e211c7a9e296eb2bf9009569886d943388e1d161d3d2cec1684e2377cda134e6b18e8368ab8b4aacf6377cda134e6b18e8368ab8b4a1ae3d9772c321bd98b32e9dce9f6fe3d9772c321bd98b32e9dce9f695e57cb5eaad998de9a060f7b3ce49af550d5b331bd340c1ef678599f3ddeeea6528c7c29fd1d123be7bbddd4ca518f853fa3a1dce0bb776bc68e91705644a44945ccc4b25f5ddc27bb29f898926f6c79fce579f434fbca43b5f3082ecf5ffec0d3ef28eadfbf282b136fdbb56c88c25bcf9a91589b7eddab644610c4a6f5dce57c37e34ed534a4979a048ece82bc69daa691f70be42e0fe75ecacd8332b8d332f707f3af6566c19939de99f40c5ea119d92e73e4c37e9b283db0528bde39edd4634fd99faca3a0e4b10a0e634fd99faca3a0e4b108ec53e5b718f5d50da66c674a3999886e047cb5e425a71d4737f79afb3a7b03f144524176953cee366dd1b48814633aaba706530af6b6d4135def8d4e0ca615ed6da787d5e645acfbfea1b9f0a88aabcc8b59f7fd4373e14fd5bfd8ff1fa13d87832222f2ff63fc7e84f61e0c88835f3f297cfec1f83746ff6d389da973fb07e0dd1bfd67f5f82e856ee6cbba4dfb9004a92b38ecb63a7d9bf71fbbdd60cf6921d3cc0f7bbcdbac19ed243a7981ef75ffde517bf45ef0303dfe7f77945efd17bc0c0f7f919513dbdfe7f7813defe667c09ef7f9fde04f7bf945f813deff3fbc09ef7f9fde04f7bfcfef027bdfe4f544f6f7f9fde04f7bf999f027bdfe7f7813defe518a27b7bfcfef027bdfe7f7813deff3fbc09ef7f941bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf94ca89edeff3fbc09ef7f9fde04f7bfcfef027bdfe526ff3fbc09ef7f9fde0129f027bdfe7f7813defe53ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f94fbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf952289edeff3fbc09ef7f9fde04f7bfcfef027bdfe556ff3fbc09ef7f9fde0129f027bdfe7f7813defe54ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f95dbfcfef027bdfe7f7804a7c09ef7f9fde04f7bf955a89edeff3fbc09ef7f9fde04f7bfcfef027bdfe57eff3fbc09ef7f9fde0129f027bdfe7f7813defe55ea27b7bfcfef027bdfe7f7813deff3fbc09ef7f965bfcfef027bdfe7f7804a7c09ef7f9fde04f7bf9e22f13147d9456df6e477121d495bca6d7537db91dc420eee97df687f7f54efe627bba5f7da1fdfd53bf954289e9eff3cfc09d37f31be04e7bfcf3f0274dfcab544f577f9efe04edbfcf5f0275dfe7bf813b6ff2c37f9f1e04f1bfcfaf0091f813beff3e7c09e37f2a3bfcfbf027adfe7e7813ceff3efc09eb7f9f9e048bf027bdfe7ef813defe663c09ef7f9fbe04f7bf954dfe7f7813deff3fbc09ef7f9fde04f7bfcfef02477813deff3fbc09ef7f333e04f7bfcfef027bdfcaaeff3fbc09ef7f9fde04f7bfcfef027bdfe7f781243c09ef7f9fde04f7bf999f027bdfe7f7813defe55b7f9fde04f7bfcfef027bdfe7f7813deff3fbc0925e04f7bfcfef027bdfcccf813deff3fbc09ef7f2afbfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf958dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f3bc0a4ade53698f7c0424bbbcf898a3ec9b3a7d9bf71e1eec18ba7181ef427bbaa0f755fdda4874f303df04defa37bbea4f7f1fde0129ef28bdfa2f78181efe5677f9fde04f7bfcfef0094f813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f3a8fc4c51f6515b7db91dc4875256f29b5d4df6e47710a3bba5f7da1fdfd53bf989eee97df687f7f54efe5437f9e5e04e7bfcf4f0272dfe7978139eff3d3c0913e04ebbfcf7f0276dfcc8f813aeff3dfc09db7f2abbfcf8f0278dfe7d78048fc09df7f9f3e04f1bf957dfe7df813d6ff3f3c09e77f9f7e04f5bfcfcf02497813deff3f7c09ef7f331e04f7bfcfdf027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7813deff3fbc09ef7f9fde0494f027bdfe7f7813defe667c09ef7f9fde04f7bf959dfe7f7813deff3fbc09ef7f9fde04f7bfcfef024a7813deff3fbc09ef7f333e04f7bfcfef027bdfcaceff3fbc09ef7f9fde04f7bfcfef027bdfe7f781253c09ef7f9fde04f7bf999f027bdfe7f7813defe5677f9fde04f7bfcfef027bdfe7f7813deff3fbc0929e04f7bfcfef027bdfcccf813deff3fbc09ef7f2b3bfcfef027bdfe7f7804a7c09ef7f9fde04f7bfa571"),
                unhex("00000001419a3b11ef91d33e075ac5200efffd7890920ac0ec03c781421be46efbc80892dd3613ab2481f2145792f4e9a77ffebc484905607601e3c0a10df2377de404496e9b09d59240f90a2bc97a79e751e7e0799c8d6ffe4aaae94f03cca8ec45e6f13fbc834eb76d3bec7eef4dfe7984588cf1912e0041c5a959cc0a7f03cce46b7ff255574a781e6547622f3789fde41a75bb69df63f77a6ff3cc22c4678c8970020e2d4ace608f685620e4fb7c4539953805824e6002af2727192185eb93f055af6199dbe6a988393edf114e654e0160939800abc9c9c648617ae4fc156bd86676f9b586b7e4f00f8d6537623a5334dc5c18c8226584d59f2b6d44d95117c8230bd20334b11d299a6e2e0c641132c26acf95b6a26ca8c056542a4770728efbf1004981eabda7b0fe7fcb5fc61af63427a8548ee0e51df7e2009303d57b4f61fcff96bf8c35ec686698de2b78b76445fb4233dad1cedb32b6f99bb7ccddbe671523b08d811aecc8bf6946bb5ccddbe666df3376f99bb7e9a2b301d47bb7ccddbe66edf3376f99bb7ccddbe66edf338b559f9dbe66edf3376f99bb7ccddbe66edf3376f99bb7e89a2c55c3376f99bb7ccddbe66edf3376f99bb7ccddbe671727e59b7ccddbe66edf3376f99bb7ccddbe66edf3376fd01d46ef76f99bb7ccddbe66edf3376f99bb7ccddbe66edfa56f9d8cddbe66edf3376f99bb7ccddbe66edf3376f99c55ed84b0dbe66edf3376f99bb7ccddbe66edf3376f99bb7e9aa3e35b23ddbe66edf3376f99bb7ccddbe66edf3376fcc69ff366df3376f99bb7ccddbe66edf3376f99bb7ccddbf4bfe6cdbe66edf3376f99bb7ccddbe66edf3376f99bb7e63928f8d6c8f76f99bb7ccddbe66edf3376f99bb7ccddbf50512cdbe66edf3376f99bb7ccddbe66edf3376f99bb7e489281416c8f76f99bb7ccddbe66edf3376f99bb7ccddbe2bd0348f76f99bb7ccddbe66edf3376f99bb7ccddbe670b796cf1dbe66edf3376f99bb7ccddbe66edf3376f99bb7c5f42ad1eedf3376f99bb7ccddbe66edf3376f99bb7cce161ef5b11eedf3376f99bb7ccddbe66edf3376f99bb7ccdcbf5006df3376f99bb7ccddbe66edf3376f99bb7ccddbe3db510eedf3376f99bb7ccddbe66edf3376f99bb7ccddbe4a951b5b23ddbe66edf3376f99bb7ccddbe66edf3376f92baab4866edf3376f99bb7ccddbe66edf3376f99bb7ccdd5bd6c47bb7ccddbe66edf3376f99bb7ccddbe66edf3373af24fe8f76f99bb7ccddbe66edf3376f99bb7ccddbe66ec106d0cddbe66edf3376f99bb7ccddbe66edf3376f99ba54d2c647bb7ccddbe66edf3376f99bb7ccddbe66edf33773a42eb647bb7ccddbe66edf3376f99bb7ccddbe66edf2db339d6df3376f99bb7ccddbe66edf3376f99bb7ccddbe6260e1b7ccddbe66edf3376f99bb7ccddbe66edf3376fdad308841ebafc65bd16c0e3cb3fc847cc2560654f3e55519381ee6125c8fbafc65bd16c0e3cb3fc847cc2560654f3e55519381ee62f4d429ac9459c022797a66a85e120f16bad303f7c39d02bcdc2fbcb16d96a4c94a868ab04a6351b231e7e1ce815e6e17de58b6cb5264a54345582531a8d91bcae0f78c71728a1b7c4be3019bb7e176df12fb7ccddbe66edf7a6f18e2e51436f897c603376fc2edbe25f6f99bb7ccddbf55fdf3b9ce95085afffffce4ffb456b8bfd6aad5289855694aace5a035ea2ec380b446617672a00c10b5fffff9c9ff68ad717fad55aa5130aad29559cb406bd45d8701688cc2ece5441c5ecd93c550760203c267942350e48d13c5041e188ebc29f052aea7703b0101e133ca11a8724689e2820f0c475e14f829577913a1a59072f2e9cc7a1850c5339af1ab32d533d80b0a3ea51e018dde6dd74374314ce6bc6accb54cf602c28fa9496ad3dae57fcf93783eec2165e37cb26d8cd2dda318f55e614b9e7c9bc1f7610b2f1be5936c6696ed18c7aaf30d4736f739178e29b5ac543af932d6ea8c8f9a81d2c9baac7087a20adad62a1d7c996b754647cd40e964dd5639a9c5b0afd59ba4f406503387036ab99b86ee652bb5e6152eee9049e80ca0670e06d573370ddcca576bcc2baa9ea30ffc1a6d7fcbb28b1c0db2c8df6a589567af30ab9e327785a44f16e41aae9a237da962559ebcc2be1291fc3f28b2fa1bd654206371479e8da30d87919340332cad27a1bd654206371479e8da30d8791934739a1f9c311fe578d1658c066e420c6dbe66e3699b90866edf3376f99c2c95e68741c56df179912b647b8da66e4219bb7ccddbe66f114326ad9cff97d1541018381baba6266efb432beab1d7f4cdadb005a639a0637574c4cddf68657d5639cded67755f7c9e894930e996be5400ed4bbfd380661613b7c4db00f611347037a1cca4de1d74e019856efcc31e0f872fa2ebea9c1aafb2a73dbc1307d2f30b2a78c66d80bbbaf932d7d9539ede0983e979856f704633176527a2f437641aafebfdd5bc242b4af30b3eb1d197d17a1bb20d57f5feeade1215a5798576d42c27fb0b6c07a62c506ac0c1bf5a981214d19342c6f66e4f469ee5e818e06e579d4c090a68c9a0692f068636e5f47523e4818e0605ec6d266eb2eab1e50d369b60485c2b818e0605ec6d266eb2eab1dc7131050e8e5f47df91a818e1119946f0f18b40cc2d2dacde5f47df91a818e1119946f0f18b40cc2b6b365e7796cca5e8e1010c6dbe379e447bb7c6847b970b647bb7ccddbf2424a91d83a6dbe379e447bb7c6847b970b647bb7ccddbf045794803f661ccbb7c78bd88f76f8ee8f7356df3376f99bb7e39deeca2744466e6acddb7ccdcc3b7c78c7bb7ccddbe6708e99d16b6f3d66edbe44c9019bb7c8399ba0b6f99bb7ccddbf202d6de7acddb7c899203376f907337416df3376f99bb7de73a3e84568ad03b7c8f12d3376f91a3374b6df3376f99bb7e46d6427f4480cdd2da4b6f99ba2b6f91e3376f99bb7ccdbdc6f920f10589699ba5b4e47bb7c9b19ba5b6f99bb7ccddbf21483c41625a66e96d391eedf26c66e96dbe66edf3376f84cddf2f3852d3adbe5154cdbe66e9d8f75047bb7ccddbe670ff2f3852d3adbe5154cdbe66e9d8f75047bb7ccddbe66df1e9c3ff75242b5b21aaa5b7ccdd491eeaa8f76f99bb7cce2165cf552d3b1eeaa85eb647ba923dd551eedf3376f99b82a3f96f954f5947baea222d91eeb88f75d47bb7ccddbe6711b2df2a9eb28f75d4445b23dd711eeba8f76f99bb7ccdc2f7000001410329a3b11eff8bf7627b1f405f2a1107fff0dc3e1bfa924f92f7a0e9296de8a6c5e2594075cb95b2e5e788c4e7a607f420d684712fffc370f86fea493e4bde83a4a5b7a29b17896501d72e56cb979e23139e981fd083a02379f81e5aed1fff2556f4a77f623904df66efec371f7e6deb8c087e609bfff2df07b3e6451040fe16ca9dab90f9a1e0591fff2556f4a77f623904df66efec371f7e6deb8c087e609bfff2df07b3e6451040fe16ca9dabc44b2880d019dc3ab9e1185529fd9fb9289d81941dbadbe719872f1ff4e3d153107280c21d5cf08c2a94fecfdc944ec0ca0edd6df38cc3978ffa71e8ea062f80324e480d92b4710f6599a00769236590d29fab7545d96117c82303d20364ad1c43d9666801da48d96434a7eadd517658602b2a1523b839477df88024c0f55ed3d87f3fe5afe30d7b1a13d42a4770728efbf1004981eabda7b0fe7fcb5fc61af6343274d1d846c08d76645fb4a35dae66edf3336f99bb7ccddbf3c78ade2dd9117ed08cf6b473b6ccadbe66edf3376f99c5f06cc0751eedf3376f99bb7ccddbe66edf3376f99bb7cce2d567e76f99bb7ccddbe66edf3376f99bb7ccddbe66edfa0ea4fcb36f99bb7ccddbe66edf3376f99bb7ccddbe66edfa331570cddbe66edf3376f99bb7ccddbe66edf3376f99c5900d49147bb7ccddbe66edf3376f99bb7ccddbe66edf338bda9228f76f99bb7ccddbe66edf3376f99bb7ccddbe6714eb612c36f99bb7ccddbe66edf3376f99bb7ccddbe66edfa6a8f8d6c8f76f99bb7ccddbe66edf3376f99bb7ccddbf30e7fcd9b7ccddbe66edf3376f99bb7ccddbe66edf3376fd2ff9b36f99bb7ccddbe66edf3376f99bb7ccddbe66edf98bc91e23376f99bb7ccddbe66edf3376f99bb7ccddbe6719547c6b647bb7ccddbe66edf3376f99bb7ccddbe66edf8f24a0505b23ddbe66edf3376f99bb7ccddbe66edf3376f8af40d23ddbe66edf3376f99bb7ccddbe66edf3376f99c2ce5b3c76f99bb7ccddbe66edf3376f99bb7ccddbe66edf17d0ab47bb7ccddbe66edf3376f99bb7ccddbe66edf3385c7bd6c47bb7ccddbe66edf3376f99bb7ccddbe66edf3372fd401b7ccddbe66edf3376f99bb7ccddbe66edf3376f8e9d443bb7ccddbe66edf3376f99bb7ccddbe66edf3376f92a546d6c8f76f99bb7ccddbe66edf3376f99bb7ccddbe46f8496ad91eedf3376f99bb7ccddbe66edf3376f99bb7c9baef0b647bb7ccddbe66edf3376f99bb7ccddbe66edf262664e4cddbe66edf3376f99bb7ccddbe66edf3376f99bb041b43376f99bb7ccddbe66edf3376f99bb7ccddbe66e8334b191eedf3376f99bb7ccddbe66edf3376f99bb7ccddce90bad91eedf3376f99bb7ccddbe66edf3376f99bb7cabcce75b7ccddbe66edf3376f99bb7ccddbe66edf3376f9898386df3376f99bb7ccddbe66edf3376f99bb7ccddbe92329e4a6e2fcdeff5f7a3a48631b4972288d8f3c54d45a7f5d7e02ae11a95a329972ec38582c33975f0e1e59bb7c4be3019bb7e710ce2308f76f99bb7cce1fbab5ef0f2cddbe25f180cddbf3886711847bb7ccddbe671e63a3413c3078cf9fffffce4ffb456b8bfd6aad5289855694aace5a035ea2ec380b446617672a00c10b5fffff9c9ff68ad717fad55aa5130aad29559cb406bd45d8701688cc2ece5447502680e725f160203c267942350e48d13c5041e188ebc29f052aea7703b0101e133ca11a8724689e2820f0c475e14f829577c33a2291532f2e9cc7a1850c5339af1ab32d533d80b0a3ea51e018dde6dd74374314ce6bc6accb54cf602c28fa9498d7240fee0053c9b4aac2fe7b0669c9193fadda318f55e614b9e7c9bc1f7610b2f1be5936c6696ed18c7aaf30d7c02eab9858f872f6f2a1d7c996b754647cd40e964dd563843d1056d6b150ebe4cb5baa323e6a074b26eab1d7444e654cff965f406503387036ab99b86ee652bb5e6152eee9049e80ca0670e06d573370ddcca576bcc2cea9fcd8480de93d091da0c996ba688df6a589567af30ab9e327785a44f16e41aae9a237da962559ebcc25b6de17ae7c2c6d8ebb7c5e644ad91f0d419c2e466edf3376f99c4d2ba543a1d576f8bcc895b23e1a83385c8cddbe66edf338344e1882d123b06efb7c6f3c88f76f8aadbe2cb6f99bb7ccddbf160b448ec1bbedf1bcf223ddbe2ab6f8b2dbe66edf3376fc93561e133661c843373566edbe66e44ad8ec23ddbe66edf3389de133661c843373566edbe66e44ad8ec23ddbe66edf3386a440ccd1e7acb85b1f99203376f8e88f730edf3376f99bb7e764e769071bd1ee82d03b7ccdccbb7c7747bb7ccddbe66e32b6bce2b2343c63dd2da4b6f99b9bb6f9073376f99bb7cce27f9c5646878c7ba5b496df337376df20e66edf3376f99b8cdc3a1d513622666e96d391eedf24066e8adbe66edf3376fcf3bc35a6b416df274510cddbe480cdd15b7ccddbe66edf16d49413a14b4b76f9455336f99ba4b6f9363376f99bb7cce29551d74ebfe3375042b5b23dd25b7c9b19bb7ccddbe66e48c392006a483466eaa85eb647ba723dd3b1eedf3376f99c53e7d44294eb6f95554b6f99ba723dd3b1eedf3376f99b9233ad211ae212ad90f152dbe66e9d8f75247bb7ccddbe67157300055d46dbe57152dbe66e9d8f75247bb7ccddbe66dc7d800000141019268ec47bf8bf7627b1f405f2a1107fff0dc3e1bfa924f92f7a0e9296de8a6c5e2594075cb95b2e5e788c4e7a607f420d684712fffc370f86fea493e4bde83a4a5b7a29b17896501d72e56cb979e23139e981fd083a02379f81e5aed1fff2556f4a77f623904df66efec371f7e6deb8c087e609bfff2df07b3e6451040fe16ca9dab90f9a1e0591fff2556f4a77f623904df66efec371f7e6deb8c087e609bfff2df07b3e6451040fe16ca9dabc44b2880d019dc3ab9e1185529fd9fb9289d81941dbadbe719872f1ff4e3d153107280c21d5cf08c2a94fecfdc944ec0ca0edd6df38cc3978ffa71e8ea062f80324e480d92b4710f6599a00769236590d29fab7545d96117c82303d20364ad1c43d9666801da48d96434a7eadd517658602b2a1523b839477df88024c0f55ed3d87f3fe5afe30d7b1a13d42a4770728efbf1004981eabda7b0fe7fcb5fc61af6343274d1d846c08d76645fb4a35dae66edf3336f99bb7ccddbf3c78ade2dd9117ed08cf6b473b6ccadbe66edf3376f99c5f06cc0751eedf3376f99bb7ccddbe66edf3376f99bb7cce2d567e76f99bb7ccddbe66edf3376f99bb7ccddbe66edfa0ea4fcb36f99bb7ccddbe66edf3376f99bb7ccddbe66edfa331570cddbe66edf3376f99bb7ccddbe66edf3376f99c5900d49147bb7ccddbe66edf3376f99bb7ccddbe66edf338bda9228f76f99bb7ccddbe66edf3376f99bb7ccddbe6714eb612c36f99bb7ccddbe66edf3376f99bb7ccddbe66edfa6a8f8d6c8f76f99bb7ccddbe66edf3376f99bb7ccddbf30e7fcd9b7ccddbe66edf3376f99bb7ccddbe66edf3376fd2ff9b36f99bb7ccddbe66edf3376f99bb7ccddbe66edf98bc91e23376f99bb7ccddbe66edf3376f99bb7ccddbe6719547c6b647bb7ccddbe66edf3376f99bb7ccddbe66edf8f24a0505b23ddbe66edf3376f99bb7ccddbe66edf3376f8af40d23ddbe66edf3376f99bb7ccddbe66edf3376f99c2ce5b3c76f99bb7ccddbe66edf3376f99bb7ccddbe66edf17d0ab47bb7ccddbe66edf3376f99bb7ccddbe66edf3385c7bd6c47bb7ccddbe66edf3376f99bb7ccddbe66edf3372fd401b7ccddbe66edf3376f99bb7ccddbe66edf3376f8e9d443bb7ccddbe66edf3376f99bb7ccddbe66edf3376f92a546d6c8f76f99bb7ccddbe66edf3376f99bb7ccddbe46f8496ad91eedf3376f99bb7ccddbe66edf3376f99bb7c9baef0b647bb7ccddbe66edf3376f99bb7ccddbe66edf262664e4cddbe66edf3376f99bb7ccddbe66edf3376f99bb041b43376f99bb7ccddbe66edf3376f99bb7ccddbe66e8334b191eedf3376f99bb7ccddbe66edf3376f99bb7ccddce90bad91eedf3376f99bb7ccddbe66edf3376f99bb7cabcce75b7ccddbe66edf3376f99bb7ccddbe66edf3376f9898386df3376f99bb7ccddbe66edf3376f99bb7ccddbe92329e4a6e2fcdeff5f7a3a48631b4972288d8f3c54d45a7f5d7e02ae11a95a329972ec38582c33975f0e1e59bb7c4be3019bb7e710ce2308f76f99bb7cce1fbab5ef0f2cddbe25f180cddbf3886711847bb7ccddbe671e63a3413c3078cf9fffffce4ffb456b8bfd6aad5289855694aace5a035ea2ec380b446617672a00c10b5fffff9c9ff68ad717fad55aa5130aad29559cb406bd45d8701688cc2ece5447502680e725f160203c267942350e48d13c5041e188ebc29f052aea7703b0101e133ca11a8724689e2820f0c475e14f829577c33a2291532f2e9cc7a1850c5339af1ab32d533d80b0a3ea51e018dde6dd74374314ce6bc6accb54cf602c28fa9498d7240fee0053c9b4aac2fe7b0669c9193fadda318f55e614b9e7c9bc1f7610b2f1be5936c6696ed18c7aaf30d7c02eab9858f872f6f2a1d7c996b754647cd40e964dd563843d1056d6b150ebe4cb5baa323e6a074b26eab1d7444e654cff965f406503387036ab99b86ee652bb5e6152eee9049e80ca0670e06d573370ddcca576bcc2cea9fcd8480de93d091da0c996ba688df6a589567af30ab9e327785a44f16e41aae9a237da962559ebcc25b6de17ae7c2c6d8ebb7c5e644ad91f0d419c2e466edf3376f99c4d2ba543a1d576f8bcc895b23e1a83385c8cddbe66edf336ff241bed04341bbedf1bcf223ddbe2ab6f8b2dbe66edf3376fc52fb410d06efb7c6f3c88f76f8aadbe2cb6f99bb7ccddbe9c12a4c948b65b5ff8f8b141aaee50cd2dddf881dd563a332cad27a1bd654206371479e8da30d87919341fb1b610d336b6c01698e6818dd5d313377da195f558ebfa66d6d802d31cd031baba6266efb432beab1e451d73baafbe4f44a49874cb5f2a0076a5dfe9c0330b09dbe26d807b089a381bd0e6526f0eba700cc2d57e6d8f07c397d175f54e0d57d9539ede0983e97985953c6336c05ddd7c996beca9cf6f04c1f4bcc2d26a1633176527a2f437641aafebfdd5bc242b4af30b3eb1d197d17a1bb20d57f5feeade1215a57985b6c12dcff616d80f4c58a0d581837eb5302429a326858decdc9e8d3dcbd031c0dcaf3a981214d193431af6b90c6dcbe8ea47c9031c0c0bd8da4cdd65d563ca1a6d36c090b857031c0c0bd8da4cdd65d5644f6a0"),
                unhex("00000001419a460845ff67d71a1a7009dd48522f4d23548358fcfcf7cb94dea7b9a9b2b9bedac66d77716374c5460b556f10a39a375f9ddac016ac8f4b73ef1b5c5694d4e42a108982b9d01d8c662b0b4e7bfa36d53951a27932964fc1000001410329a460845ffa5807572a8354835e18d7ee90315eab4de2066ae6fdf91cdaf8ef637547026ff88f87afe92533495d4c01d5a9c8028600377673d114526efd568d920d4900000141019269182117fffa5807572a8354835e18d7ee90315eab4de2066ae6fdf91cdaf8ef637547026ff88f87afe92533495d4c01d5a9c8028600377673d114526efd56821f07dc28a21c501b801d85df46363e3e71"),
                unhex("00000001419a660845fffa580103000001410329a660845ffa58010300000141019269982117fffa580103"),
                unhex("00000001419a860845fffa580103000001410329a860845ffa5801030000014101926a182117fffa580103"),
            )
        },
    },
    "vp8": {
        "YUV420P": {
            (24, 16): (
                unhex("1003009d012a1800100000070885858899848800281013ad501fc01fd01050122780feffbb029ffffa2546bd18c06f7ffe8951fffe8951af46301bdfffa22a00"),
            ),
            (320, 240): (
                unhex("3012009d012a4001f000004708858588998488020200061604f70681649f6bdb9b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387b27387af400feffa3de00"),
                unhex("d102000b116000180018582ff400088004335fad724f9c730000"),
                unhex("d102000b112800180018582ff400088004335fad724f9c730000"),
                unhex("d102000b10f800180018582ff400088004335fad724f9c730000"),
                unhex("d102000b10cc00180018582ff400088004335fad724f9c730000"),
            )
        },
    },
    "vp9": {
        "YUV420P": {
            (24, 16): (
                unhex("8249834200017000f60038241c18000000200000047ffffffba9da00059fffffff753b413bffffffeea7680000"),
            ),
            (128, 128): (
                unhex("824983420007f007f60c38241c18900000f047d8fd620cdbe9cd6f5721d7b400006b31c5aa6ce921164007ec134d6336dae2f48372f5c44a826c5b54a60ff0451c1b136691c51b2830f807c83155903eeb2d0926d4f000"),
                unhex("860040929c444f8000037000005a0480"),
                unhex("860040929c444f8000037000005a0480"),
                unhex("860040929c344c8000037000005a0480"),
                unhex("860040929c2c49e000037000005a0480"),
            ),
        },
        "YUV444P": {
            (24, 16): (
                unhex("a249834200002e001ec007048383000000040000223fffffeea76800c7ffffffeea7680677ffffff753b40081000"),
            ),
            (128, 128): (
                unhex("a24983420000fe00fec1870483831200001e47d8fd620cdbe9cd6f5721d7b400006b31c5aa6ce921164007ec134d6336dae2f48372f5c44a826c5b54a60ff0451c1b136691c51b2830f807c80efd58636455d6695137c000"),
                unhex("a60040929c444f8000037000005a0480"),
                unhex("a60040929c444f8000037000005a0480"),
                unhex("a60040929c344c8000037000005a0480"),
                unhex("a60040929c2c49e000037000005a0480"),
            )
        },
        "YUV444P10": {
            (128, 128): (
                unhex("b124c1a100003f803fb061c120e0c48000140047d8fdf83ef11f47913f8fd8f0e0be8318fdf7f897fdbf918f84ff549eafe6"
                      "775f9717fc611a40006727ffe6478ffdd9a0bffbb3417feecd0b3fff32fbffbb3e0ffdd9f07fdd9f31fff997dffdd9f07fee"
                      "cf83feeccaeffdd9a0bffbb3417feecd05ffbb3423ff767c1ffbb3e0ffbb3e0ffbb3e0ffdd9f07feecf83feecf83feecd4c1"
                      "ff8e077e7db18d89caf903bae28eb4acb433b81b7c8c71ad1b3eff6bf9fd250bbbf2bba9675ae65948029eca9893a4c867aa"
                      "224210c55b61f8cffc703bf3ed8c6c4e57c81dd71475a565a19dc0dbe4638d68d9f7fb5fcfe9285ddf95dd4b3ad732ca4014"
                      "f654c49d26433d511210862adb0fc67feecf83feecf83ff767c1ff767c1ffbb3e0ffbb3e0ffdd9f07fdd9a36fe9285ddf95d"
                      "d4b3ad732ca4014f654c49d26433d511210862adb0fc67f4942eefcaeea59d6b9965200a7b2a624e93219ea8890843156d87"
                      "e33fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029eca9893a4c867"
                      "aa224210c55b61f8cffdd9f07fdd9f07fdd9f07fdd9f07fdd9f07fdd9f07fdd9f07fdd9b1cffdd9a0bffbb3417feecd05ffb"
                      "b3423ff767c1ffbb3e0ffbb3e0ffbb3e0ffdd9f07feecf83feecf83feeccaeffdd9a0bffbb3417feecd05ffbb3423ff767c1"
                      "ffbb3e0ffbb3e0ffbb3e0ffdd9f07feecf83feecf83feecd1b7f4942eefcaeea59d6b9965200a7b2a624e93219ea88908431"
                      "56d87e33fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029eca9893a"
                      "4c867aa224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc67feecf83feecf83feecf8"
                      "3feecf83feecf83feecf83feecf83feecd1b7f4942eefcaeea59d6b9965200a7b2a624e93219ea8890843156d87e33fa4a17"
                      "77e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029eca9893a4c867aa224210c"
                      "55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc67feecf83feecf83feecf83feecf83feecf8"
                      "3feecf83feecf83feece09bff1c0efcfb631b1395f20775c51d695968677036f918e35a367dfed7f3fa4a1777e57752ceb5c"
                      "cb290053d9531274990cf544484218ab6c3f19ff8e077e7db18d89caf903bae28eb4acb433b81b7c8c71ad1b3eff6bf9fd25"
                      "0bbbf2bba9675ae65948029eca9893a4c867aa224210c55b61f8cffdd9f07fdd9f07feecf83feecf83ff767c1ff767c1ffbb"
                      "3e0ffbb3405fd250bbbf2bba9675ae65948029eca9893a4c867aa224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c"
                      "49d26433d511210862adb0fc67f4942eefcaeea59d6b9965200a7b2a624e93219ea8890843156d87e33fa4a1777e57752ceb"
                      "5ccb290053d9531274990cf544484218ab6c3f19ffbb3e0ffbb3e0ffbb3e0ffbb3e0ffbb3e0ffbb3e0ffbb3e0ffbb346fffc"
                      "703bf3ed8c6c4e57c81dd71475a565a19dc0dbe4638d68d9f7fb5fcfe9285ddf95dd4b3ad732ca4014f654c49d26433d5112"
                      "10862adb0fc67fe381df9f6c636272be40eeb8a3ad2b2d0cee06df231c6b46cfbfdafe7f4942eefcaeea59d6b9965200a7b2"
                      "a624e93219ea8890843156d87e33ff767c1ff767c1ffbb3e0ffbb3e0ffdd9f07fdd9f07feecf83feecd017f4942eefcaeea5"
                      "9d6b9965200a7b2a624e93219ea8890843156d87e33fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19"
                      "fd250bbbf2bba9675ae65948029eca9893a4c867aa224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d51"
                      "1210862adb0fc67feecf83feecf83feecf83feecf83feecf83feecf83feecf83feecd367f4942eefcaeea59d6b9965200a7b"
                      "2a624e93219ea8890843156d87e33fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9"
                      "675ae65948029eca9893a4c867aa224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc6"
                      "7feecf83feecf83feecf83feecf83feecf83feecf83feecf83feecd017f4942eefcaeea59d6b9965200a7b2a624e93219ea8"
                      "890843156d87e33fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029e"
                      "ca9893a4c867aa224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc67feecf83feecf8"
                      "3feecf83feecf83feecf83feecf83feecf83feecd017f4942eefcaeea59d6b9965200a7b2a624e93219ea8890843156d87e3"
                      "3fa4a1777e57752ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029eca9893a4c867aa"
                      "224210c55b61f8cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc67feecf83feecf83feecf83feecf8"
                      "3feecf83feecf83feecf83feecd017f4942eefcaeea59d6b9965200a7b2a624e93219ea8890843156d87e33fa4a1777e5775"
                      "2ceb5ccb290053d9531274990cf544484218ab6c3f19fd250bbbf2bba9675ae65948029eca9893a4c867aa224210c55b61f8"
                      "cfe9285ddf95dd4b3ad732ca4014f654c49d26433d511210862adb0fc67feecf83feecf83feecf83feecf83feecf83feecf8"
                      "3feecf83feecc400"),
                unhex("b30020494e2227c00002807d230000002b7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98fffffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa7d5fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa600"),
                unhex("b30020494e2227c00001807000002b7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98fffffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa7d5fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa600"),
                unhex("b30020494e2227c00001807000002b7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98fffffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa7d5fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa600"),
                unhex("b30020494e2227c00001807000002b7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98fffffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffffffdbefd4ffffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffffff6fbf53fffffffedf7ea7fffffffdbefd4ffffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa7d5fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7e9f57fffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63fffffdbefd31fffffedf7e98ffffff6fbf4c7fffffb7dfa63ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa9ffffff6fbf53fffffedf7ea7fffffdbefd4ffffffb7dfa600"),
            )
        },
    },
    "av1": {
        "YUV420P": {
            (64, 64): (
                unhex("12000a0b00000002affff036be40103219110100010000004b17c531ecb5321932af9b2fab54ee58012c"),
            ),
        },
    },
}

TEST_PICTURES: dict[str, dict[tuple[int, int], TEST_DATA]] = {
    "png": {
        (32, 32): (
            unhex("89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000002849444154785eedd08100000000c3a0f9531fe4855061c0800103060c183060c0800103060cbc0f0c102000013337932a0000000049454e44ae426082"),
            unhex("89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18eda30000002549444154785eedd03101000000c2a0f54fed610d884061c0800103060c183060c080810f0c0c20000174754ae90000000049454e44ae426082"),
        ),
    },
    "png/L": {
        (32, 32): (
            unhex("89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000000274524e5300ff5b9122b50000002049444154785e63fccf801f3011906718550009a1d170180d07e4bc323cd20300a33d013f95f841e70000000049454e44ae426082"),
            unhex("89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"),
        ),
    },
    "png/P": {
        (32, 32): (
            unhex("89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b330f4880000010074524e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0053f707250000001c49444154785e63f84f00308c2a0087c068384012c268388ca87000003f68fc2e077ed1070000000049454e44ae426082"),
            unhex("89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000330000660000990000cc0000ff0000003300333300663300993300cc3300ff3300006600336600666600996600cc6600ff6600009900339900669900999900cc9900ff990000cc0033cc0066cc0099cc00cccc00ffcc0000ff0033ff0066ff0099ff00ccff00ffff00000033330033660033990033cc0033ff0033003333333333663333993333cc3333ff3333006633336633666633996633cc6633ff6633009933339933669933999933cc9933ff993300cc3333cc3366cc3399cc33cccc33ffcc3300ff3333ff3366ff3399ff33ccff33ffff33000066330066660066990066cc0066ff0066003366333366663366993366cc3366ff3366006666336666666666996666cc6666ff6666009966339966669966999966cc9966ff996600cc6633cc6666cc6699cc66cccc66ffcc6600ff6633ff6666ff6699ff66ccff66ffff66000099330099660099990099cc0099ff0099003399333399663399993399cc3399ff3399006699336699666699996699cc6699ff6699009999339999669999999999cc9999ff999900cc9933cc9966cc9999cc99cccc99ffcc9900ff9933ff9966ff9999ff99ccff99ffff990000cc3300cc6600cc9900cccc00ccff00cc0033cc3333cc6633cc9933cccc33ccff33cc0066cc3366cc6666cc9966cccc66ccff66cc0099cc3399cc6699cc9999cccc99ccff99cc00cccc33cccc66cccc99ccccccccccffcccc00ffcc33ffcc66ffcc99ffccccffccffffcc0000ff3300ff6600ff9900ffcc00ffff00ff0033ff3333ff6633ff9933ffcc33ffff33ff0066ff3366ff6666ff9966ffcc66ffff66ff0099ff3399ff6699ff9999ffcc99ffff99ff00ccff33ccff66ccff99ccffccccffffccff00ffff33ffff66ffff99ffffccffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023faca40000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"),
        ),
    },
    "jpeg": {
        (32, 32): (
            unhex("ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"),
            unhex("ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"),
        ),
        (128, 128): (
            unhex("ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080080008003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f40a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803fffd9"),
        )
    },
    "jpega": {
        (32, 32): (
            (
                unhexlify("ffd8ffe000104a46494600010100000100010000ffdb004300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb004301ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00011080020002003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00651451400514514005145140051451401fffd9ffd8ffe000104a46494600010100000100010000ffdb004300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0000b080020002001011100ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffda0008010100003f006514514514514514514514515fffd9"),
                {"quality": 0, "alpha-offset": 642},
            ),
            (
                unhexlify("ffd8ffe000104a46494600010100000100010000ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffdb00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc00011080020002003011100021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00fe3fe800a002800a002800a002800a002800a002800a002800a002800a00ffd9ffd8ffe000104a46494600010100000100010000ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc0000b080020002001011100ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffda0008010100003f00fe3fe8a28a28a28a28a28a28a28affd9"),
                {"quality": 100, "alpha-offset": 655},
            ),
        )
    },
    "webp": {
        (32, 32): (
            unhex("524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"),
            unhex("524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"),
        ),
        (128, 128): (
            unhex("524946468600000057454250565038580a000000100000007f00007f0000414c50480a0000000107509088084444ff035650382056000000d007009d012a800080002e9968b45a22a828280800984b4b76ff817a05e00fd0091f506c40478a31f01eb66666666666666666666666666666666666666666666666666666650000fef7973fff61f9eb5ebd44000000"),
        ),
    },
    "avif": {
        (32, 32): (
            # with alpha, quality = 90%:
            unhex("00000020667479706176696600000000617669666d6966316d6961664d4131410000018d6d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000002c696c6f630000000044000002000100000001000001c900000017000200000001000001b5000000140000004269696e660000000000020000001a696e6665020000000001000061763031436f6c6f72000000001a696e6665020000000002000061763031416c706861000000001a69726566000000000000000e6175786c000200010001000000c3697072700000009d6970636f0000001469737065000000000000002000000020000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c78000200020002800000000e706978690000000001080000000c6176314381001c0000000038617578430000000075726e3a6d7065673a6d706567423a636963703a73797374656d733a617578696c696172793a616c706861000000001e69706d6100000000000000020001040102830400020401058607000000336d64617412000a0518113ff6153209100043f41c96deb55c12000a0538113ff609320c1000004b68891db6ff6afff0"),
            # without alpha:
            unhex("00000020667479706176696600000000617669666d6966316d6961664d413141000000f26d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000001e696c6f6300000000440000010001000000010000011a000000170000002869696e660000000000010000001a696e6665020000000001000061763031436f6c6f72000000006a697072700000004b6970636f0000001469737065000000000000002000000020000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c78000200020002800000001769706d610000000000000001000104010283040000001f6d64617412000a0538113ff609320c10000043f1949e508e4effe8"),
        ),
        (128, 128): (
            unhex("00000020667479706176696600000000617669666d6966316d6961664d4131410000018d6d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000002c696c6f630000000044000002000100000001000001d100000021000200000001000001b50000001c0000004269696e660000000000020000001a696e6665020000000001000061763031436f6c6f72000000001a696e6665020000000002000061763031416c706861000000001a69726566000000000000000e6175786c000200010001000000c3697072700000009d6970636f0000001469737065000000000000008000000080000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c78000200020002800000000e706978690000000001080000000c6176314381001c0000000038617578430000000075726e3a6d7065673a6d706567423a636963703a73797374656d733a617578696c696172793a616c706861000000001e69706d6100000000000000020001040102830400020401058607000000456d64617412000a061819bfff61503210100000c1b55a2a0ddca5d848d670758012000a063819bfff60903215100000c32e26eb1e02e2bff997cca3e3c87a330adc"),
            unhex("00000020667479706176696600000000617669666d6966316d6961664d413141000000f26d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000001e696c6f6300000000440000010001000000010000011a000000200000002869696e660000000000010000001a696e6665020000000001000061763031436f6c6f72000000006a697072700000004b6970636f0000001469737065000000000000008000000080000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c78000200020002800000001769706d61000000000000000100010401028304000000286d64617412000a063819bfff60903214100000c1b4ca69c7b685dffb415abcd34052bcc4"),
        ),
    }
}


def makebuf(size, b=0x20) -> bytearray:
    d = (chr(b).encode("latin1"))*size
    return bytearray(d)


def h2b(s) -> bytes:
    return binascii.unhexlify(s)


def convert_pixel(rgbx="00112233", fmt="BGRX") -> str:
    pixel = ""
    for color in fmt:
        index = "RGBX".index(color)         # "RGBX".index("R") -> 0
        pixel += rgbx[index*2:(index+1)*2]  # 0 -> "00"
    return pixel


assert convert_pixel(rgbx="01234567", fmt="RGBX") == "01234567"
assert convert_pixel(rgbx="01234567", fmt="BGRX") == "45230167"


def make_test_image(pixel_format: str, w: int, h: int, plane_values: Iterable[int] | str = (0x20, 0x80, 0x80, 0x0)):
    # pylint: disable=import-outside-toplevel
    from xpra.codecs.image import ImageWrapper
    from xpra.codecs.constants import get_subsampling_divs

    def makeimage(pixels, **kwargs) -> ImageWrapper:
        kwargs["thread_safe"] = True
        kwargs["pixel_format"] = kwargs.get("pixel_format", pixel_format)
        kwargs["depth"] = kwargs.get("bytesperpixel", 4)*8
        return ImageWrapper(0, 0, w, h, pixels, **kwargs)

    if pixel_format.startswith("YUV") or pixel_format.startswith("GBRP") or pixel_format == "NV12":
        divs = get_subsampling_divs(pixel_format)
        try:
            depth = int(pixel_format.split("P")[1])         # ie: YUV444P10 -> 10
        except (IndexError, ValueError):
            depth = 8
        Bpp = roundup(depth, 8)//8
        nplanes = 2 if pixel_format == "NV12" else 3
        strides = tuple(w//divs[i][0]*Bpp for i in range(nplanes))
        sizes = tuple(strides[i]*h//divs[i][1]*Bpp for i in range(nplanes))
        if isinstance(plane_values, Iterable):
            planes = tuple(struct.pack(b"B", plane_value) * sizes[i]
                           for i, plane_value in enumerate(plane_values[:nplanes]))
        else:
            planes = tuple(makebuf(sizes[i]) for i in range(nplanes))
        return makeimage(planes, rowstride=strides, planes=nplanes)
        # l = len(y)+len(u)+len(v)
    if pixel_format in ("RGB", "BGR", "RGBX", "BGRX", "XRGB", "BGRA", "RGBA", "r210", "BGR48", "YUYV"):
        Bpp = len(pixel_format)
        if pixel_format == "BGR48":
            Bpp = 6
        stride = w*Bpp
        if isinstance(plane_values, str):
            rgb_data = h2b(plane_values) * w * h
        else:
            rgb_data = bytes(makebuf(stride*h))
        if pixel_format == "YUYV":
            stride = w * 2
        return makeimage(rgb_data, bytesperpixel=Bpp, rowstride=stride)
    raise ValueError(f"don't know how to create a {pixel_format} image")


def testdecoder(decoder_module, full: bool) -> Sequence[str]:
    dtype = decoder_module.get_type()
    codecs = list(decoder_module.get_encodings())
    for encoding in tuple(codecs):
        try:
            testdecoding(decoder_module, encoding, full)
        except Exception as e:
            log(f"{dtype}: {encoding} decoding failed", exc_info=True)
            log.warn(f"{dtype}: {encoding} decoding failed: {e}")
            del e
            codecs.remove(encoding)
    if not codecs:
        log.error(f"{dtype}: all the codecs have failed! {csv(decoder_module.get_encodings())}")
    return tuple(codecs)


def testdecoding(decoder_module, encoding: str, full: bool) -> None:
    test_data_set: dict[str, dict[tuple[int, int], TEST_DATA]] | None = TEST_COMPRESSED_DATA.get(encoding)
    for cs in decoder_module.get_input_colorspaces(encoding):
        min_w, min_h = decoder_module.get_min_size(encoding)
        test_data: dict[tuple[int, int], TEST_DATA] = {}
        if test_data_set:
            test_data = test_data_set.get(cs, {})
        elif encoding in TEST_PICTURES:
            # maybe this is a picture format:
            test_data = TEST_PICTURES[encoding]
        # add a context init test, without any data to decode:
        test_data.setdefault((256, 128), ())
        for size, frames in test_data.items():
            w, h = size
            if w < min_w or h < min_h:
                log(f"skipped {encoding} decoding test at {w}x{h} for {decoder_module} (min size is {min_w}x{min_h})")
                continue
            try:
                decoder = decoder_module.Decoder()
                decoder.init_context(encoding, w, h, cs, typedict())
            except Exception:
                log.error(f"Error creating context {encoding} {w}x{h} {cs}")
                raise
            try:
                if frames:
                    log(f"{decoder_module.get_type()}: testing {encoding} / {cs} with {len(frames)} frames of size {w}x{h}")
                    for i, (data, options) in enumerate(frames):
                        try:
                            log(f"frame {i+1} is {len(data or ()):5} bytes")
                            image = decoder.decompress_image(data, typedict(options))
                            if image is None:
                                raise RuntimeError(f"failed to decode test data for encoding {encoding!r} with colorspace {cs!r}")
                            if image.get_width()!=w:
                                raise RuntimeError(f"expected image of width {w} but got {image.get_width()}")
                            if image.get_height()!=h:
                                raise RuntimeError(f"expected image of height {h} but got {image.get_height()}")
                            log(f" test passed for {w}x{h} {encoding} - {cs}")
                        except Exception:
                            log.error(f"Error on {encoding} {w}x{h} test {cs} frame {i}")
                            raise
                if full:
                    log(f"{decoder_module.get_type()}: testing {encoding} / {cs} with junk data")
                    # test failures:
                    options = typedict({"junk": True})
                    try:
                        image = decoder.decompress_image(b"junk", options)
                    except (RuntimeError, ValueError):
                        image = None
                    if image is not None:
                        raise RuntimeError(f"decoding junk with {decoder_module.get_type()} should have failed, got {image} instead")
            finally:
                decoder.clean()


def testencoder(encoder_module, full: bool) -> Sequence[str]:
    etype = encoder_module.get_type()
    codecs = list(encoder_module.get_encodings())
    for encoding in tuple(codecs):
        try:
            testencoding(encoder_module, encoding, full)
        except Exception as e:
            log(f"{etype}: {encoding} encoding failed", exc_info=True)
            log.warn(f"Warning: {etype} encoder testing failed with {encoding}:")
            log.warn(f" {e}")
            del e
            codecs.remove(encoding)
    if not codecs:
        log.error(f"{etype}: all the codecs have failed! ({csv(encoder_module.get_encodings())})")
    return tuple(codecs)


def testencoding(encoder_module, encoding: str, full: bool) -> None:
    # test a bit bigger so we exercise more code:
    W, H = DEFAULT_TEST_SIZE
    do_testencoding(encoder_module, encoding, W, H, full)


def get_encoder_max_sizes(encoder_module) -> tuple[int, int]:
    w, h = TEST_LIMIT_W, TEST_LIMIT_H
    for encoding in encoder_module.get_encodings():
        ew, eh = get_encoder_max_size(encoder_module, encoding)
        w = min(w, ew)
        h = min(h, eh)
    return w, h


def get_encoder_max_size(encoder_module, encoding: str,
                         limit_w: int = TEST_LIMIT_W, limit_h: int = TEST_LIMIT_H) -> tuple[int, int]:
    # probe to find the max dimensions:
    # (it may go higher, but we don't care as windows can't)
    etype = encoder_module.get_type()

    def einfo() -> str:
        return f"{etype} {encoding} {encoder_module.get_version()}"

    def elog(s, *args):
        log(f"{einfo()} "+s, *args)
    log("get_encoder_max_size%s", (encoder_module, encoding, limit_w, limit_h))
    maxw = w = 512
    while w<=limit_w:
        try:
            do_testencoding(encoder_module, encoding, w, 128)
            maxw = w
            w *= 2
        except Exception as e:
            elog(f"is limited to max width={max} for {encoding}")
            log(f" {e}")
            del e
            break
    elog(f"max width={maxw}")
    maxh = h = 512
    while h <= limit_h:
        try:
            do_testencoding(encoder_module, encoding, 128, h)
            maxh = h
            h *= 2
        except Exception as e:
            elog(f"is limited to max height={maxh} for {encoding}")
            log(f" {e}")
            del e
            break
    elog(f"max height={maxh}")
    # now try combining width and height
    # as there might be a lower limit based on the total number of pixels:
    MAX_WIDTH, MAX_HEIGHT = maxw, maxh
    # start at half:
    v = max(512, min(maxw, maxh)//2)
    while v < max(limit_w, limit_h):
        for tw, th in ((v, v), (v*2, v)):
            if tw > limit_w or th > limit_h:
                continue
            try:
                w = min(maxw, tw)
                h = min(maxh, th)
                do_testencoding(encoder_module, encoding, w, h)
                elog(f"can handle {w}x{h} for {encoding}")
                MAX_WIDTH, MAX_HEIGHT = w, h
            except Exception as e:
                elog(f"is limited to {MAX_WIDTH}x{MAX_HEIGHT} for {encoding}")
                log(f" {e}")
                del e
                break
        v *= 2
    elog(f"max dimensions for {encoding}: {MAX_WIDTH}x{MAX_HEIGHT}")
    return MAX_WIDTH, MAX_HEIGHT


def do_testencoding(encoder_module, encoding, W: int, H: int, full: bool = False,
                    limit_w: int = TEST_LIMIT_W, limit_h: int = TEST_LIMIT_H) -> None:
    for cs_in in encoder_module.get_input_colorspaces(encoding):
        for cs_out in encoder_module.get_output_colorspaces(encoding, cs_in):
            for spec in encoder_module.get_specs(encoding, cs_in):
                test_encoder_spec(spec.codec_class, encoding, cs_in, cs_out, W, H, full, limit_w, limit_h)


def test_encoder_spec(encoder_class: Callable, encoding: str, cs_in: str, cs_out: str, W:int, H:int, full:bool=False,
                      limit_w: int = TEST_LIMIT_W, limit_h: int = TEST_LIMIT_H) -> None:
    log(f"testing {encoding} using {encoder_class}: {cs_in} to {cs_out}")
    e = None
    try:
        e = encoder_class()
        etype = e.get_type()
        options = typedict({
            # "b-frames": True,
            "dst-formats": [cs_out],
            "quality": 50,
            "speed": 50,
        })
        e.init_context(encoding, W, H, cs_in, options)
        N = 5
        data = meta = None
        for i in range(N):
            options = typedict()
            image = make_test_image(cs_in, W, H)
            v = e.compress_image(image, options)
            if v is None:
                raise RuntimeError(f"{encoding} compression failed on image {i+1} of {N}")
            data, meta = v
            if not data:
                delayed = meta.get("delayed", 0)
                assert delayed>0, "data is empty and there are no delayed frames!"
                if i>0:
                    # now we should get one:
                    data, meta = e.flush(delayed)
        del image
        if data is None:
            raise RuntimeError(f"None data for {etype} using {encoding} encoding with {cs_in} / {cs_out}")
        if not data:
            raise RuntimeError(f"no compressed data for {etype} using {encoding} encoding with {cs_in} / {cs_out}")
        if meta is None:
            raise RuntimeError(f"missing metadata for {etype} using {encoding} encoding with {cs_in} / {cs_out}")
        log(f"{etype}: {encoding} / {cs_in} / {cs_out} passed")
        if full:
            wrong_formats = [x for x in ("YUV420P", "YUV444P", "BGRX", "r210") if x!=cs_in]
            # log("wrong formats (not %s): %s", cs_in, wrong_formats)
            if wrong_formats:
                wrong_format = wrong_formats[0]
                try:
                    image = make_test_image(wrong_format, W, H)
                    out = e.compress_image(image, options=options)
                except Exception:
                    out = None
                if out is not None:
                    raise RuntimeError(f"encoder {etype} should have failed using {encoding} encoding with {wrong_format} instead of {cs_in} / {cs_out}")
            for w, h in ((W//2, H//2), (W*2, H//2), (W//2, H**2)):
                if w > limit_w or h > limit_h:
                    continue
                try:
                    image = make_test_image(cs_in, w, h)
                    out = e.compress_image(image, options=options)
                except Exception:
                    out = None
                if out is not None:
                    raise RuntimeError(f"encoder {etype}, info={e.get_info()} should have failed using {encoding} encoding with invalid size {w}x{h} vs {W}x{H}")
    finally:
        if e:
            e.clean()


def testcsc(csc_module, full=False, test_cs_in=None, test_cs_out=None) -> None:
    W = 48
    H = 32
    log("test_csc%s", (csc_module, full, test_cs_in, test_cs_out))
    do_testcsc(csc_module, W, H, W, H, full, test_cs_in, test_cs_out)
    if full:
        do_testcsc(csc_module, W, H, W*2, H*2, full, test_cs_in, test_cs_out)
        do_testcsc(csc_module, W, H, W//2, H//2, full, test_cs_in, test_cs_out)


def get_csc_max_size(converter, test_cs_in=None, test_cs_out=None,
                     limit_w: int = TEST_LIMIT_W, limit_h: int = TEST_LIMIT_H) -> tuple[int, int]:
    # probe to find the max dimensions:
    # (it may go higher but we don't care as windows can't)
    MAX_WIDTH, MAX_HEIGHT = 512, 512
    cs = converter
    # as there might be a lower limit based on the total number of pixels:
    v = 512
    while v <= min(limit_w, limit_h):
        for tw, th in ((v, v), (v*2, v)):
            if tw > limit_w or th > limit_h:
                break
            try:
                do_testcsc(cs, tw, th, tw, th, False, test_cs_in, test_cs_out, limit_w, limit_h)
                log(f"{cs} can handle {tw}x{th}")
                MAX_WIDTH, MAX_HEIGHT = tw, th
            except Exception:
                log(f"{cs} is limited to {MAX_WIDTH}x{MAX_HEIGHT} for {test_cs_in} -> {test_cs_out}")
                break
        v *= 2
    log(f"{cs} max dimensions: {MAX_WIDTH}x{MAX_HEIGHT}")
    return MAX_WIDTH, MAX_HEIGHT


def do_testcsc(csc_module, iw: int, ih: int, ow: int, oh: int,
               full: bool = False, test_cs_in=None, test_cs_out=None,
               limit_w: int = TEST_LIMIT_W, limit_h: int = TEST_LIMIT_H) -> None:
    log("do_testcsc%s", (csc_module, iw, ih, ow, oh, full, test_cs_in, test_cs_out,
                         TEST_LIMIT_W, TEST_LIMIT_H))
    cstype = csc_module.get_type()
    cs_in_list = test_cs_in
    if cs_in_list is None:
        cs_in_list = csc_module.get_input_colorspaces()
    for cs_in in cs_in_list:
        cs_out_list = test_cs_out
        if cs_out_list is None:
            cs_out_list = csc_module.get_output_colorspaces(cs_in)
        for cs_out in cs_out_list:
            if iw != ow or ih != oh:
                spec = csc_module.get_spec(cs_in, cs_out)
                if not spec.can_scale:
                    continue
            log(f"{cstype}: testing {cs_in} -> {cs_out}")
            e = csc_module.Converter()
            try:
                e.init_context(iw, ih, cs_in, ow, oh, cs_out, typedict())
                image = make_test_image(cs_in, iw, ih)
                out = e.convert_image(image)
                if out.get_width() != ow:
                    raise RuntimeError(f"expected image of width {ow} but got {out.get_width()}")
                if out.get_height() != oh:
                    raise RuntimeError(f"expected image of height {oh} but got {out.get_height()}")
                if out.get_pixel_format() != cs_out:
                    raise RuntimeError(f"expected pixel format {cs_out} but got {out.get_pixel_format()}")
                if full:
                    for w,h in ((iw*2, ih//2), (iw//2, ih**2)):
                        if w > limit_w or h > limit_h:
                            continue
                        try:
                            image = make_test_image(cs_in, w, h)
                            out = e.convert_image(image)
                        except Exception:
                            out = None
                        if out is not None:
                            raise RuntimeError(f"converting an image of a smaller size with {cstype} should have failed, got {out} instead")
            finally:
                e.clean()
